package com.yohann.boot.config.filter;

import com.yohann.boot.common.exception.GlobalException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * ThreadLocal拦截器用于remove线程数据
 *
 * @author Yeung Wah
 * @since 2021/11/14
 */
@Slf4j
@Configuration
@ConditionalOnProperty("threadLocal.holder.className")
public class ThreadLocalInterceptor extends HandlerInterceptorAdapter {

    private final List<ThreadLocal<?>> THREAD_LOCAL_VARIABLES = new LinkedList<>();

    @Value("#{'${threadLocal.holder.className}'.split(',')}")
    public void collectThreadLocalVariables(Set<String> classNames) {
        for (String className : classNames) {
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                GlobalException.throwOut(e.getMessage(), e);
            }

            //获取所有ThreadLocal字段值
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Object o = null;
                try {
                    o = field.get(null);
                } catch (IllegalAccessException ignored) {
                }
                if (o instanceof ThreadLocal) {
                    THREAD_LOCAL_VARIABLES.add((ThreadLocal<?>) o);
                }
            }
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        THREAD_LOCAL_VARIABLES.forEach(ThreadLocal::remove);
        log.info("ThreadLocal is cleared");
    }
}